package com.leyou.trade.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.auth.utils.UserContext;
import com.leyou.common.dto.PageDTO;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.trade.dto.OrderDetailDTO;
import com.leyou.trade.dto.OrderDetailDTO;
import com.leyou.trade.dto.OrderFormDTO;
import com.leyou.trade.dto.OrderWithDetailDTO;
import com.leyou.trade.dto.OrderListDTO;
import com.leyou.trade.entity.Order;
import com.leyou.trade.entity.OrderDetail;
import com.leyou.trade.entity.OrderLogistics;
import com.leyou.trade.enums.OrderStatus;
import com.leyou.trade.mapper.OrderMapper;
import com.leyou.trade.service.OrderDetailService;
import com.leyou.trade.service.OrderLogisticsService;
import com.leyou.trade.service.OrderService;
import com.leyou.trade.utils.PayHelper;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import feign.FeignException;
import io.jsonwebtoken.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.leyou.common.constants.MQConstants.ExchangeConstants.ORDER_EXCHANGE_NAME;
import static com.leyou.common.constants.MQConstants.ExchangeConstants.SMS_EXCHANGE_NAME;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.EVICT_ORDER_KEY;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.VERIFY_CODE_KEY;

/**
 * @author 虎哥
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private OrderDetailService detailService;

    @Autowired
    private UserClient userClient;

    @Autowired
    private OrderLogisticsService logisticsService;

    @Autowired
    private PayHelper payHelper;

    @Autowired
    private AmqpTemplate amqpTemplate;


    @Transactional
    @Override
    public Long createOrder(OrderFormDTO orderFormDTO) {
        // 1.订单数据
        Order order = new Order();
        // 1.1.总金额
        Map<Long, Integer> carts = orderFormDTO.getCarts();
        // 1.1.1.获取所有sku的Id
        Set<Long> ids = carts.keySet();
        // 1.1.2.查询sku
        List<SkuDTO> skuList = itemClient.querySkuByIds(ids);
        // 1.1.3.计算总金额
        long total = 0;
        for (SkuDTO sku : skuList) {
            total += sku.getPrice() * carts.get(sku.getId());
        }
        order.setTotalFee(total);
        // 1.2.邮费和实付金额，应该去另一个微服务查询，根据运费模板计算运费
        order.setPostFee(0L);
        order.setActualFee(total + order.getPostFee());
        // 1.3.支付类型
        order.setPaymentType(orderFormDTO.getPaymentType());
        // 1.4.用户id
        Long userId = UserContext.getUser().getId();
        order.setUserId(userId);
        // 1.5.订单状态
        order.setStatus(OrderStatus.INIT.getValue());

        save(order);
        Long orderId = order.getOrderId();

        // 2.订单详情数据
        List<OrderDetail> details = new ArrayList<>(skuList.size());
        // 2.1.遍历sku
        for (SkuDTO sku : skuList) {
            // 2.2.把sku信息封装到OrderDetail
            OrderDetail detail = new OrderDetail();
            detail.setPrice(sku.getPrice());
            detail.setOrderId(orderId);
            detail.setNum(carts.get(sku.getId()));
            detail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
            detail.setSpec(sku.getSpecialSpec());
            detail.setTitle(sku.getTitle());
            detail.setSkuId(sku.getId());
            details.add(detail);
        }
        // 2.3.批量新增
        detailService.saveBatch(details);

        // 3.订单物流数据
        // 3.1.根据地址id，查询收货地址
        AddressDTO address = userClient.queryAddressById(orderFormDTO.getAddressId());
        // 3.2.把地址封装到OrderLogistics中
        OrderLogistics logistics = address.toEntity(OrderLogistics.class);
        logistics.setOrderId(orderId);
        // 3.3.新增
        logisticsService.save(logistics);

        // 4.减库存
        try {
            itemClient.deductStock(carts);
        } catch (FeignException e) {
            throw new LyException(e.status(), e.contentUTF8());
        }

        // TODO 5.扣减优惠券

        // 6.发送延迟消息
        amqpTemplate.convertAndSend(ORDER_EXCHANGE_NAME, EVICT_ORDER_KEY, orderId);
        return orderId;
    }

    @Override
    public String getPayUrl(Long id) {
        // 1.查询订单
        Order order = getById(id);
        // 2.健壮性处理
        if(order == null){
            // 订单不存在
            throw new LyException(400, "订单编号错误！");
        }
        // 订单状态是否是未支付
        if(!Objects.equals(order.getStatus(), OrderStatus.INIT.getValue())){
            // 订单已经支付或关闭
            throw new LyException(400, "订单已经支付或关闭");
        }

        // 3.查询订单详情
        List<OrderDetail> list = detailService.query().eq("order_id", id).list();
        String desc = list.get(0).getTitle();
        // 4.生成url
        return payHelper.getPayUrl(id, order.getActualFee(), desc);
    }

    @Override
    @Transactional
    public void handleNotify(Map<String, String> data) {
        try {
            // 1.签名校验
            payHelper.checkResponseSignature(data);
            // 2.通信标示校验
            payHelper.checkReturnCode(data);
            // 3.业务标示校验
            payHelper.checkResultCode(data);

            // 4.订单金额校验
            // 4.1.获取结果中的订单编号和支付金额
            String orderIdStr = data.get("out_trade_no");
            String totalFee = data.get("total_fee");
            // 4.2.非空判断
            if(StringUtils.isBlank(orderIdStr) || StringUtils.isBlank(totalFee)){
                // 数据有误
                throw new LyException(400, "请求参数有误！");
            }
            // 4.3.查询真实订单
            Long orderId = Long.valueOf(orderIdStr);
            Order order = getById(orderId);
            // 4.4.比较金额
            if(!StringUtils.equals(totalFee, order.getActualFee().toString())){
                // 如果不一致，证明是有人来捣乱
                throw new LyException(400, "订单金额有误，再捣乱我报警了！");
            }

            // 5.幂等校验（通知是否已经处理）
            if(!Objects.equals(order.getStatus(), OrderStatus.INIT.getValue())){
                // 订单已经处理过了，重复的通知
                return;
            }

            // 6.修改订单状态（未支付 --> 已支付）
            // update tb_order set status = 2 where order_id = 1124124 AND status = 1
            update()
                    .set("status", OrderStatus.PAY_UP.getValue())
                    .eq("order_id", orderId)
                    .eq("status", OrderStatus.INIT.getValue())
                    .update();

            log.info("微信回调通知处理成功！");
        } catch (RuntimeException e) {
            log.error("微信回调通知处理失败", e.getMessage());
            throw e;
        }
    }

    @Override
    public Integer queryOrderStatusById(Long orderId) {
        Order order = getById(orderId);
        if (order == null) {
            throw new LyException(400, "订单不存在！");
        }
        return order.getStatus();
    }

    @Override
    @Transactional
    public void evictOrder(Long orderId) {

        // 1.查询订单
        Order order = getById(orderId);
        if(order == null){
            return;
        }
        // 2.判断订单状态
        if(!Objects.equals(order.getStatus(), OrderStatus.INIT.getValue())) {
            // 3.如果已支付或关闭，结束
            return;
        }
        // 4.如果未支付，关闭订单 update tb_order set status = 5 where order_id = 1 AND status = 1
        boolean b = update()
                .set("status", OrderStatus.CLOSED.getValue())
                .eq("order_id", orderId)
                .eq("status", OrderStatus.INIT.getValue())
                .update();
        if(!b){
            // 关闭失败，结束
            return;
        }
        // 5.查询订单详情
        List<OrderDetail> details = detailService.query().eq("order_id", orderId).list();
        if (CollectionUtils.isEmpty(details)) {
            return;
        }
        // 解析出sku的ID和num
        Map<Long, Integer> skuMap = details.stream()
                .collect(Collectors.toMap(OrderDetail::getSkuId, OrderDetail::getNum));
        // 6.恢复库存
        try {
            itemClient.plusStock(skuMap);
        } catch (FeignException e) {
            throw new LyException(e.status(), e.contentUTF8());
        }

    }

    private int DEFAULT_PAGE_SIZE = 10;
    /**
     * 获取指定状态的订单展示页列表
     * @param userId 用户id
     * @param status 订单状态
     * @return
     */
    @Override
    public PageDTO<OrderListDTO> getOrderByUserIdAndStatus(Long userId, Integer status,Integer page) {
        Page<Order> orderList = null ;
        //1.获取指定状态的order列表
        if (status == 0){
            orderList = query().eq("user_id", userId).orderByDesc("create_time").page(new Page<>(page,DEFAULT_PAGE_SIZE));
        }else {
            orderList = query().eq("user_id", userId).eq("status", status).orderByDesc("create_time").page(new Page<>(page,DEFAULT_PAGE_SIZE));
        }
        //判断查询结果是否为空
        if (CollectionUtils.isEmpty(orderList.getRecords())){
            return new PageDTO<OrderListDTO>(0L, 0L, null);
        }
        //2.获取orderId列表
        List<Long> orderIds = orderList.getRecords().stream().map(Order::getOrderId).collect(Collectors.toList());
        //3.根据orderIds查询OrderDetail
        List<OrderDetail> orderDetails = detailService.query().in("order_id", orderIds).list();

        //4.转orderDetailDto后，按orderId分类
        Map<Long, List<OrderDetailDTO>> orderDetailMap = OrderDetailDTO.convertEntityList(orderDetails)
                .stream().collect(Collectors.groupingBy(OrderDetailDTO::getOrderId));

        //5.将orderList装维orderListDTO，最后给每个dto设置OrderDetail
        List<OrderListDTO> orderListDTOS = OrderListDTO.convertEntityList(orderList.getRecords());
        orderListDTOS.forEach(x->x.setOrderDetails(orderDetailMap.get(x.getOrderId())));

        return new PageDTO<OrderListDTO>(orderList.getTotal(), orderList.getPages(), orderListDTOS);
    }

    @Override
    public Boolean cancelOrder(Long orderId) {
        return update().eq("status",1).eq("order_id",orderId).set("status",5).update();
    }

    @Override
    public PageDTO<OrderWithDetailDTO> queryOrderByKey( String key, Integer status, Integer page, Integer rows ) {
        //判断用户是否登录
        //当用户登录时，用户信息会保存在上下文域中
        Long id = UserContext.getUser().getId();
        if(id == null){
            throw new LyException(401,"用户未登录，请先登录！");
        }

        Page<Order> orderPage = null;
        //如果key存在，就先查orderDetail表
        if(StringUtils.isNotBlank(key)){
            //标题关键字查询，
            List<OrderDetail> title = detailService.query().like("title", key).list();
            //把在orderDetail表里查到的订单的所有订单编号取出来放在一个集合，会有重复,要去重所以用set集合，自动去重
            HashSet<Long> orderIdSet = new HashSet<>();
            for (OrderDetail orderDetail:title){
                orderIdSet.add(orderDetail.getOrderId());
            }
            //根据已经查到的订单的订单编号去order表查数据
           orderPage = query().eq("user_id",id).in("order_id",orderIdSet).eq(status !=null,"status",status).orderByDesc("create_time").page(new Page<>(page,rows));
        }else {
            //如果key不存在，直接去order表搜索
           orderPage = query().eq("user_id",id).eq(status !=null,"status",status).orderByDesc("create_time").page(new Page<>(page,rows));
        }

        List<Order> orders = orderPage.getRecords();
        long pages = orderPage.getPages();
        long total = orderPage.getTotal();
        if(CollectionUtils.isEmpty(orders)){
            // 不存在
           throw new LyException(404,"没有找到相关订单");
        }
        //封装
        //1.先把order进行封装，封装为orderwithDetailDTO
        List<OrderWithDetailDTO> orderWithDetailDTOS = OrderWithDetailDTO.convertEntityList(orders);
        for (OrderWithDetailDTO orderWithDetailDTO : orderWithDetailDTOS){
            //把查到的订单根据订单id去补充详情
            // orderDetail的数据补充orderWithDetailDTO
            List<OrderDetail> orderDetails = detailService.query().eq("order_id", orderWithDetailDTO.getOrderId()).list();
           //转换为DTO
            List<OrderDetailDTO> orderDetailDTOS = OrderDetailDTO.convertEntityList(orderDetails);

            for (OrderDetailDTO orderDetailDTO : orderDetailDTOS){
                //拿到orderDetail表里的规格参数，json类型
                String spec = orderDetailDTO.getSpec();
                //序列化
                Map<Long, Object> specMap = JsonUtils.toMap(spec, Long.class, Object.class);
                //拿到map的key,就是规格参数的id
                Set<Long> specKeySet = specMap.keySet();
                //根据map集合拿到的key去查具体的规格参数值
              List<SpecParamDTO> specParamDTOS = itemClient.querySpecBySpecIds(specKeySet);
              /*  List<SpecParam> specParams = paramService.listByIds(specKeySet);
                List<SpecParamDTO> specParamDTOS = SpecParamDTO.convertEntityList(specParams);*/

                Map<String,Object> specNameWithValue = new HashMap<>();
               for (SpecParamDTO specParamDTO : specParamDTOS){
                   //把orderDetail表里规格参数的id对应的规格参数数据拿到
              specNameWithValue.put(specParamDTO.getName(),specMap.get(specParamDTO.getId()));
               }
               //反序列化
                String json = JsonUtils.toJson(specNameWithValue);
               //把拿到的规格参数补充到orderDetail表里
                orderDetailDTO.setSpec(json);
            }
            //最后再把orderDetail表的数据补充到存储订单表和订单详情表的新表里
           orderWithDetailDTO.setOrderDetails(orderDetailDTOS);
        }
        return new PageDTO<>(total,pages,orderWithDetailDTOS);
    }

    //发货提醒
    @Override
    public void sendMsg( Long orderId ) {
        // 模拟查询订单
        Map<String,String> msg = new HashMap<>(2);
        msg.put("phone", "18121040540");
        String code = orderId.toString().substring(0, 6);
        msg.put("code", code);
        amqpTemplate.convertAndSend(SMS_EXCHANGE_NAME, VERIFY_CODE_KEY, msg);
    }

    ////订单查询
   /* @Override
    public PageDTO<OrderDetail> queryOrderByKey( String key ) {
        // 健壮性判断
        int currentPage = 1;
        int size = 10;
        //订单id
        //title字段
       *//* Order order = getById(Long.parseLong(key));
        if (order == null) {
            throw new LyException(400, "订单不存在！");
        }*//*
        boolean isKeyExists = StringUtils.isNotBlank(key);

        Page<OrderDetail> orderId = detailService.query()
                .like(isKeyExists,"title","%"+key+"%")
                .or()
                .eq(isKeyExists,"order_id", key)
                .page(new Page<>(currentPage, size));
        List<OrderDetail> records = orderId.getRecords();
        long total = orderId.getTotal();
        long pages = orderId.getPages();

        return new PageDTO<>(total,pages,records);
    }*/
}